using System;
using System.Collections.Generic;
using System.Linq;


namespace HEFramework
{
    /// <summary>
    ///
    /// 世界（管理单例）（管理场景）
    /// 
    /// ----------------------------------------
    /// 作者 hep
    /// 创建时间：2023-11-29 14:23:20
    /// ----------------------------------------
    /// </summary>
    public class World
    {
        [StaticField] private static World instance;

        [StaticField]
        public static World Instance
        {
            get { return instance ??= new World(); }
        }

        private readonly Dictionary<Type, ASingleton> singletons = new();
        private readonly Dictionary<eSceneType, Scene> scenes = new();
        public EntitySystem EntitySystem { get; private set; }


        private World()
        {
        }

        public void Awake()
        {
            //添加实例
            AddSingleton<Logger>();
            AddSingleton<TimeRunner>();
            AddSingleton<IDGenerater>();
            AddSingleton<ObjectGenerater>();

            AddSingleton<AssetBundleLoader>();
            AddSingleton<AssetBundleDownloader>();
            AddSingleton<AssetLoader>();
            AddSingleton<SceneLoader>();
            AddSingleton<AudioSourcePlayer>();
            AddSingleton<UILoader>();
            AddSingleton<DataTableLoader>();

            //代码加载
            AddSingleton<CodeLoader>();
            EntitySystem = new();
            //创建场景
            CreateScene(eSceneType.Main);
        }

        public void Update()
        {
            TimeRunner.Instance.Update();
            AssetBundleLoader.Instance.Update();
            AssetLoader.Instance.Update();
            AudioSourcePlayer.Instance.Update();
            EntitySystem.Update();
        }

        public void LateUpdate()
        {
            EntitySystem.LateUpdate();
        }

        public void Dispose()
        {
            instance = null;
            lock (this)
            {
                foreach (var scene in scenes.Values)
                {
                    scene.Dispose();
                }

                scenes.Clear();

                List<ASingleton> singletons = this.singletons.Values.ToList();
                for (int i = singletons.Count - 1; i >= 0; i--)
                {
                    singletons[i].Dispose();
                }

                this.singletons.Clear();
            }
        }

        public T AddSingleton<T>() where T : ASingleton, ISingletonAwake, new()
        {
            T singleton = new();
            singleton.Awake();

            AddSingleton(singleton);
            return singleton;
        }

        public T AddSingleton<T, A>(A _a) where T : ASingleton, ISingletonAwake<A>, new()
        {
            T singleton = new();
            singleton.Awake(_a);

            AddSingleton(singleton);
            return singleton;
        }

        public T AddSingleton<T, A, B>(A _a, B _b) where T : ASingleton, ISingletonAwake<A, B>, new()
        {
            T singleton = new();
            singleton.Awake(_a, _b);

            AddSingleton(singleton);
            return singleton;
        }

        public T AddSingleton<T, A, B, C>(A _a, B _b, C _c) where T : ASingleton, ISingletonAwake<A, B, C>, new()
        {
            T singleton = new();
            singleton.Awake(_a, _b, _c);

            AddSingleton(singleton);
            return singleton;
        }

        public void AddSingleton(ASingleton _singleton)
        {
            lock (this)
            {
                Type type = _singleton.GetType();
                singletons[type] = _singleton;
            }

            _singleton.Register();
        }

        public void CreateScene(eSceneType _type, object _message = null)
        {
            Scene scene;
            scenes.TryGetValue(_type, out scene);
            if (scene == null)
            {
                scenes.Add(_type, new Scene(_type, _message));
            }
        }

        public Scene GetScene(eSceneType _type)
        {
            Scene scene;
            scenes.TryGetValue(_type, out scene);
            return scene;
        }

        public void DestroyScene(eSceneType _type)
        {
            Scene scene;
            scenes.TryGetValue(_type, out scene);
            if (scene != null)
            {
                scene.Dispose();
                scenes.Remove(_type);
            }
        }

        public void DestroyScene(IScene _type)
        {
            Scene scene;
            scenes.TryGetValue(_type.SceneType, out scene);
            if (scene != null)
            {
                scene.Dispose();
                scenes.Remove(_type.SceneType);
            }
        }
    }
}